/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#include "EntityInterpolator.hpp"

#include <OGRE/Ogre.h>

#include "HelpDefs.hpp"
#include "Landscape.hpp"

namespace spelprog2 {
const Ogre::Radian OGRE_PI_RAD(OGRE_PI);

namespace presentation {

EntityInterpolator::EntityInterpolator(const LandscapeSpatials & spatials, Ogre::int32 queryFlag,
        Ogre::SceneManager & sceneMan, const Ogre::String & model,
        Ogre::Real modelScale, const Ogre::Vector3 & initPos, Ogre::Radian initYaw) :
    spatials_(spatials),
    sceneManager_(sceneMan),
    sceneNode_(),
    entity_(),
    progressions_()
{
    sceneNode_ = sceneMan.getRootSceneNode()->createChildSceneNode();
    entity_ = sceneMan.createEntity(model);
    entity_->setQueryFlags(queryFlag);
    entity_->getSubEntity(0)->setCustomParameter(0, Ogre::Vector4(1, 1, 1, 0));
    sceneNode_->attachObject(entity_);
    sceneNode_->scale(modelScale, modelScale, modelScale);

    setPosition(initPos);
    setYaw(initYaw);
}

EntityInterpolator::~EntityInterpolator()
{
    sceneManager_.destroySceneNode(sceneNode_);
}

Ogre::Vector3 EntityInterpolator::convertPosition(const sim::Int3d & pos)
{
    return Ogre::Vector3(
        Ogre::Real(pos.x) / Ogre::Real(sim::SCENE_TO_WORLD_SCALER),
        Ogre::Real(pos.y) / Ogre::Real(sim::SCENE_TO_WORLD_SCALER),
        Ogre::Real(pos.z) / Ogre::Real(sim::SCENE_TO_WORLD_SCALER));
}

Ogre::Vector3 EntityInterpolator::convertPosition(const sim::Int2d & pos, const LandscapeSpatials & spatials)
{
    Ogre::Vector3 pos3d;
    pos3d.x = Ogre::Real(pos.x) / Ogre::Real(sim::SCENE_TO_WORLD_SCALER);
    pos3d.z = Ogre::Real(pos.y) / Ogre::Real(sim::SCENE_TO_WORLD_SCALER);
    pos3d.y = spatials.getAltitude(Ogre::Vector2(pos3d.x, pos3d.z));
    return pos3d;
}

Ogre::Radian EntityInterpolator::convertAngle(sim::Int angle)
{
    return Ogre::Radian(Ogre::Real(angle) / Ogre::Real(sim::ANGULAR_UNITS_PER_HALFCIRCLE) * OGRE_PI);
}

void EntityInterpolator::setEntityData(const Ogre::String & key, const Ogre::Any & data)
{
    entity_->getUserObjectBindings().setUserAny(key, data);
}

void EntityInterpolator::gfxUpdate(float deltaTime)
{
    if (!entity_->getAllAnimationStates()) {
        return;
    }

    for (Ogre::ConstEnabledAnimationStateIterator it = entity_->getAllAnimationStates()->getEnabledAnimationStateIterator();
            it.hasMoreElements(); it.moveNext()) {
        
        //play current animation
        Ogre::AnimationState * const anim = *it.current();
        anim->addTime(deltaTime);

        //see if animation has finished
        if (!anim->hasEnded()) {
            continue;
        }

        //since finished, we will look for another animation to play
        ProgressionMap::iterator jt = progressions_.find(anim->getAnimationName());
        if (jt == progressions_.end()) {
            continue;
        }

        //anim->setEnabled(false); //TODO: perhaps better to leave enabled? study behaviour

        //look up and consume key for the next animation found
        Ogre::AnimationState * const nextAnim = entity_->getAnimationState(jt->second);
        progressions_.erase(jt);
        if (!nextAnim) {
            //this means either and invalid or empty animation was specified - this means we will simply stay put
            continue;
        }

        //play next animation once (if it lasts longer than that it will loop unless manually added)
        nextAnim->setTimePosition(0);
        nextAnim->setLoop(false);
        nextAnim->setEnabled(true);
    }
}

void EntityInterpolator::setPosition(const Ogre::Vector3 & newPosition)
{
    position_ = newPosition;

    Ogre::Vector3 adjusted(newPosition);
    adjusted.y += entity_->getBoundingBox().getHalfSize().y;

    sceneNode_->setPosition(adjusted);

    //TODO: update roll and pitch based on landscape?
    /*const Ogre::Radian roll = spatials_.getInclination(Ogre::Vector2(adjusted.x, adjusted.z), Ogre::Vector2(1, 0));
    setRoll(-roll);
    const Ogre::Radian pitch = spatials_.getInclination(Ogre::Vector2(adjusted.x, adjusted.z), Ogre::Vector2(0, 1));
    setPitch(-pitch);*/
}

void EntityInterpolator::setRoll(Ogre::Radian newAngle)
{
    Ogre::Radian angleDiff = newAngle - sceneNode_->getOrientation().getRoll();
    sceneNode_->roll(angleDiff, Ogre::Node::TS_WORLD);
}

void EntityInterpolator::setPitch(Ogre::Radian newAngle)
{
    Ogre::Radian angleDiff = newAngle - sceneNode_->getOrientation().getPitch();
    sceneNode_->pitch(angleDiff, Ogre::Node::TS_WORLD);
}

void EntityInterpolator::setYaw(Ogre::Radian newAngle)
{
    Ogre::Radian angleDiff = newAngle - sceneNode_->getOrientation().getYaw();
    sceneNode_->yaw(angleDiff, Ogre::Node::TS_WORLD);
}

void EntityInterpolator::setSelectionMarker(SelectionMarker marker)
{
    Ogre::Vector4 color;
    switch (marker) {
        case SELECTIONMARK_OFF:
            color = Ogre::Vector4(1, 1, 1, 0);
            break;
        case SELECTIONMARK_NEUTRAL:
            color = Ogre::Vector4(1, 1, 0, 1);
            break;
        case SELECTIONMARK_FRIENDLY:
            color = Ogre::Vector4(0, 1, 0, 1);
            break;
        case SELECTIONMARK_ENEMY:
            color = Ogre::Vector4(1, 0, 0, 1);
            break;
        case SELECTIONMARK_ALLY:
            color = Ogre::Vector4(0, 0, 1, 1);
            break;
    }

    entity_->getSubEntity(0)->setCustomParameter(0, color);
}

void EntityInterpolator::enableLoopingAnimation(const Ogre::String & name, Ogre::Real startTime)
{
    Ogre::AnimationState * const anim = entity_->getAnimationState(name);
    if (anim) {
        if (!anim->getEnabled()) {
            anim->setTimePosition(startTime);
        }

        anim->setLoop(true);
        anim->setEnabled(true);

        ProgressionMap::iterator it = progressions_.find(name);
        if (it != progressions_.end()) {
            progressions_.erase(it);
        }
    }
}

void EntityInterpolator::enableSingleAnimation(const Ogre::String & name, bool reset)
{
    Ogre::AnimationState * const anim = entity_->getAnimationState(name);
    if (anim) {
        if (reset) {
            anim->setTimePosition(0);
        }

        anim->setLoop(false);
        anim->setEnabled(true);

        ProgressionMap::iterator it = progressions_.find(name);
        if (it != progressions_.end()) {
            progressions_.erase(it);
        }
    }
}


void EntityInterpolator::synchronizeAnimations(Ogre::String & name1, Ogre::String & name2)
{
    Ogre::AnimationState * const anim1 = entity_->getAnimationState(name1), 
                         * const anim2 = entity_->getAnimationState(name2);
    if (anim1 && anim2) {
        if (!anim1->getEnabled()) {
            anim2->setTimePosition(anim1->getTimePosition());
        }
    }
}

void EntityInterpolator::setAnimationWeight(const Ogre::String & name, Ogre::Real weight)
{
    Ogre::AnimationState * const anim = entity_->getAnimationState(name);
    if (anim) {
        anim->setWeight(weight);
    }
}

void EntityInterpolator::endAnimation(const Ogre::String & name)
{
    Ogre::AnimationState * const anim = entity_->getAnimationState(name);
    if (anim) {
        anim->setEnabled(false);

        //make sure there is no key
        ProgressionMap::iterator it = progressions_.find(name);
        if (it != progressions_.end()) {
            progressions_.erase(it);
        }
    }
}

void EntityInterpolator::beginSingleAnimation(const Ogre::String & name, const Ogre::String & follower)
{
    //deal with progressions:
    if (follower != "") {
        //we got a new intended key (which may be invalid - invalid key = just stop animation in update())
        progressions_[name] = follower;
    }
    else {
        //make sure there is no key
        ProgressionMap::iterator it = progressions_.find(name);
        if (it != progressions_.end()) {
            progressions_.erase(it);
        }
    }
}

}; //presentation
}; //spelprog2
